﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Threading;

namespace NanoMvvm.Collections
{
    public class ObservableSortedCollection<T> : SortedList<T>, IObservableList<T>
    {
        [NonSerialized]
        private readonly SynchronizationContext synchronizationContext;

        public ObservableSortedCollection()
            : this(Comparer<T>.Default, NanoMvvmFramework.Instance.Synchronization.Main)
        {
        }

        public ObservableSortedCollection(IComparer<T> comparer)
            : this(comparer, NanoMvvmFramework.Instance.Synchronization.Main)
        {
        }

        public ObservableSortedCollection(IComparer<T> comparer, SynchronizationContext synchronizationContext)
            : base(comparer)
        {
            this.synchronizationContext = synchronizationContext;
        }

        [field: NonSerialized]
        public event NotifyCollectionChangedEventHandler CollectionChanged;

        [field: NonSerialized]
        public event PropertyChangedEventHandler PropertyChanged;

        protected override void OnInsertAt(int index, T item)
        {
            if (synchronizationContext != null)
            {
                synchronizationContext.Send(state => InsertAtImpl(index, item), null);
            }
            else
            {
                InsertAtImpl(index, item);
            }

            OnPropertyChanged(new PropertyChangedEventArgs("Count"));
            OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
        }

        protected override void OnRemoveAt(int index)
        {
            if (synchronizationContext != null)
            {
                synchronizationContext.Send(state => RemoveAtImpl(index), null);
            }
            else
            {
                RemoveAtImpl(index);
            }

            OnPropertyChanged(new PropertyChangedEventArgs("Count"));
            OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
        }

        protected override void OnClear()
        {
            if (synchronizationContext != null)
            {
                synchronizationContext.Send(state => ClearImpl(), null);
            }
            else
            {
                ClearImpl();
            }

            OnPropertyChanged(new PropertyChangedEventArgs("Count"));
            OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
        }

        protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            var handler = CollectionChanged;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            var handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        private void InsertAtImpl(int index, T item)
        {
            base.OnInsertAt(index, item);

            // check where it was placed
            index = IndexOf(item);

            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index));
        }

        private void RemoveAtImpl(int index)
        {
            var item = this[index];

            base.OnRemoveAt(index);

            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index));
        }

        private void ClearImpl()
        {
            base.OnClear();
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
        }
    }
}